<?php
/**
 * Created by PhpStorm.
 * User: asus
 * Date: 2018/9/27
 * Time: 11:26
 */

namespace App\Libs\Common;


class ImgCompress {
    private $src;                 // 源图地址
    private $image;
    private $imageInfo;         // 图片信息
    public $percent = 0.5;      // 缩放比例
    public $customizeWidth = 0;           // 自定义宽度
    public $customizeHeight = 0;          // 自定义高度
    public $imgSuffix = ['.jpg', '.jpeg', '.png', '.bmp', '.wbmp','.gif'];      // 可用后缀

    /**
     * ImgCompress constructor.
     * @param $src  源图
     * @param int $percent   压缩比例
     */
    public function __construct($src,$percent=0)
    {
        $this->src = $src;
        if(!empty($percent)){
            $this->percent = $percent;
        }
    }

    /*********   调用方法   *********/

    /**
     * 高清压缩图片
     * @param string $saveName 提供图片名（可不带扩展名，用源图扩展名）用于保存。或不提供文件名直接显示
     * @return bool 返回图片绝对地址
     */
    public function compressImg($saveName='')
    {
        $this->_openImage();
        if(!empty($saveName)) {
            // 保存
           return $this->_saveImage($saveName);
        } else{
            $this->_showImage();
        }
    }

    /**
     * 保存图片并返回图片地址
     * @param string $directoryType 目录属性storage 或者 public
     * @param string $imgName   文件名
     * @param int $width    自定义宽
     * @param int $height   自定义高
     * @return bool|string  成功返回简易目录地址，失败返回false
     */
    public function ImageSaveAddress($directoryType = 'storage',$imgName='',$width = 0,$height = 0)
    {
        if((empty(is_numeric($width)) || empty(is_numeric($height))) && ($width < 0 || $height < 0) ){
            return false;
        }else{
           $widthHeight = $this->widthHeight($width,$height);
           if($widthHeight){
               if(!empty($widthHeight['percent'])) $this->percent=$widthHeight['percent'];
               if(!empty($widthHeight['customizeWidth'])) $this->customizeWidth=$widthHeight['customizeWidth'];
               if(!empty($widthHeight['customizeHeight'])) $this->customizeHeight=$widthHeight['customizeHeight'];
           }
        }

        if (!in_array($directoryType, array('storage', 'public'))) {
            return false;
        }
        // 文件地址 及 文件名称
        if(empty($imgName)){
            // 文件名称
            $imgName = str_random(26).date('YmdHis');
        }
        $directory = base_path();
        if($directoryType == 'public'){
            $directory .= '/public/';
        }else{
            $directory .= '/storage/app/public/';
        }
        $folder = $directory.'images/'.date('Y').'/'.date('m');
        if (!file_exists($folder)) {       // 判断文件夹 不存在者创建
            mkdir($folder, 0755, true);
        }
        $imgAddress = $directory.'images/'.date('Y').'/'.date('m').'/'.$imgName;
        // 判断是否开启 imagick 扩展，如果开启 使用 imagick 压缩图片
        if(extension_loaded('imagick')){
            $compressImgAddress = $this->imagickCompressImg($imgAddress);
        }else{
            $compressImgAddress =  $this->gdCompressImg($imgAddress);
        }
        if($compressImgAddress){
            return trim(str_replace($directory,'',$compressImgAddress));
        }
        return false;
    }

    /*********   执行方法   *********/

    /**
     * 压缩高宽,或者比例
     * @param $inputWidth
     * @param $inputHeight
     * @return bool
     */
    public function widthHeight($inputWidth,$inputHeight){
        if((empty(is_numeric($inputWidth)) || empty(is_numeric($inputHeight))) && ($inputWidth < 0 || $inputHeight < 0) ){
            return false;
        }
        list($width, $height, $type, $attr) = getimagesize($this->src); // 取得图像大小
        $imageInfo = array(
            'width' => $width,
            'height' => $height,
            'type' => image_type_to_extension($type,false), //取得图像类型的文件后缀
            'attr' => $attr,
        );
        $res = [];
        if(!empty($inputWidth) && empty($inputHeight)){
            $res['percent'] = $inputWidth/$imageInfo['width'];
        }elseif(empty($inputWidth) && !empty($inputHeight)){
            $res['percent'] = $inputHeight/$imageInfo['height'];
        }elseif (!empty($inputWidth) && !empty($inputHeight)){
            $res['customizeWidth'] = $inputWidth;
            $res['customizeHeight'] = $inputHeight;
        }
        return $res;
    }

    /**
     * 打开图片
     */
    private function _openImage(){
        list($width, $height, $type, $attr) = getimagesize($this->src); // 取得图像大小
        $this->imageInfo = array(
            'width' => $width,
            'height' => $height,
            'type' => image_type_to_extension($type,false), //取得图像类型的文件后缀
            'attr' => $attr,
        );
        $fun = "imagecreatefrom".$this->imageInfo['type'];  // imagecreatefromgif — 由文件或 URL 创建一个新图象。  php 方法
        $this->image = $fun($this->src);
    }

    /**
     * 返回高宽
     * @return array
     */
    private function _imageWH(){
        $imgNewWH = [
            'width' => $this->imageInfo['width'] * $this->percent,
            'height' => $this->imageInfo['height'] * $this->percent,
        ];
        if($this->customizeWidth && $this->customizeHeight){
            $imgNewWH = [
                'width' => $this->customizeWidth,
                'height' => $this->customizeHeight,
            ];
        }
        return $imgNewWH;
    }

    /**
     * 操作图片
     */
    private function _thumpImage(){
        $imgNewWH = $this->_imageWH();
        $image_thump = imagecreatetruecolor($imgNewWH['width'],$imgNewWH['height']);    // 创建画布
        //将原图复制带图片载体上面，并且按照一定比例压缩,极大的保持了清晰度
        imagecopyresampled($image_thump,$this->image,0,0,0,0,$imgNewWH['width'],$imgNewWH['height'],$this->imageInfo['width'],$this->imageInfo['height']);  // 重采样拷贝部分图像并调整大小
        imagedestroy($this->image); // 销毁一图像
        $this->image = $image_thump;
    }

    /**
     * 输出图像 保存图片则用saveImage()
     */
    private function _showImage(){
        header('Content-Type: image/'.$this->imageInfo['type']);
        $func = "image".$this->imageInfo['type'];
        $func($this->image);
    }

    /**
     * 新图片地址 及名称
     * @param $dstImgName
     * @return bool|string
     */
    private function _imageType($dstImgName){
        if(empty($dstImgName)) return false;
        $dstExt = strrchr($dstImgName ,".");
        $sourceText = strrchr($this->src ,".");
        if(!empty($dstExt)) $dstExt =strtolower($dstExt);
        if(!empty($sourceText)) $sourceText =strtolower($sourceText);

        if(!empty($dstExt) && in_array($dstExt,$this->imgSuffix)){
            //有指定文件名及扩展名
            $dstName = $dstImgName;
        }elseif(!empty($sourceText) && in_array($sourceText,$this->imgSuffix)){
            // 如果目标图片名有后缀就用目标图片扩展名 后缀，如果没有，则用源图的扩展名
            $dstName = $dstImgName.$sourceText;
        }else{
            $dstName = $dstImgName.'.'.$this->imageInfo['type'];
        }
        return $dstName;
    }

    /**
     * 保持图片
     * @param $dstImgName   1、可指定字符串不带后缀的名称，使用源图扩展名 。2、直接指定目标图片名带扩展名。
     * @return bool
     */
    private function _saveImage($dstImgName){
        $dstName = $this->_imageType($dstImgName);
        if(empty($dstName)) return false;
        $func = "image".$this->imageInfo['type'];
        $func($this->image,$dstName);
        if(!file_exists($dstName)){
            return false;
        }
        return $dstName;
    }



    /**
     * GD式压缩图片
     * @param $imgAddress   图片存放地址及名称
     * @return bool
     */
    private function gdCompressImg($imgAddress){
        $this->_openImage();    // 打开图片
        $this->_thumpImage();   // 压缩图片
        $compressImgAddress =  $this->_saveImage($imgAddress);
        return $compressImgAddress;
    }

    /*********  使用imagick 扩展 压缩图片  *********/

    /**
     * 使用imagick 压缩图片,
     * @param $imgAddress
     * @return bool|string
     */
    private function imagickCompressImg($imgAddress){
        if (preg_match("/^(http:\/\/|https:\/\/).*$/", $this->src)) {
            // 判断是网络图片
            $grabImage = $this->grabImage($this->src);
            if(empty($grabImage)) return false;
            $this->src = $grabImage;
        }
        if(!file_exists($this->src)) return false;  // 判断文件是否存在

        $this->_openImage();    // 打开图片
        $imgNewWH = $this->_imageWH();  // 获取压缩高宽
        $dstName = $this->_imageType($imgAddress);
        if(empty($dstName)) return false;

        $image = new \Imagick($this->src);      // 不能取得网络地址图片
        $image = $image->coalesceImages();
        foreach ($image as $frame) {
            $frame->thumbnailImage($imgNewWH['width'], $imgNewWH['height']);
        }
        $image = $image->optimizeImageLayers();
        $ImageSave = $image->writeImages($dstName, true);
        if(empty($ImageSave)) return false;
        if(!file_exists($dstName)) return false;
        return $dstName;
    }

    /**
     * 返回临时文件地址
     * @param $url
     * @return bool|string
     */
    public function grabImage($url){
        if (!preg_match("/^(http:\/\/|https:\/\/).*$/", $url)) return false;    // 判断是否为网络地址
        $ext = strrchr($url, ".");  // 获取文件格式
        if(empty($ext)) return false;
        if(!in_array($ext,$this->imgSuffix)) return false;
        $filename = str_random(9).date("YmdHis");     //文件名
        ob_start();//打开输出
        readfile($url);//输出图片文件
        $img = ob_get_contents();//得到浏览器输出
        ob_end_clean();//清除输出并关闭

        $imgPath = tempnam(sys_get_temp_dir(), $filename);   // 创建临时文件
        rename($imgPath, $imgPath .= $ext);         // 重命名文件或目录.
        $fp = @fopen($imgPath, "w");        // 打开临时文件，写入
        @fwrite($fp, $img);     // 函数写入文件
        fclose($fp);        // 函数关闭一个打开文件。
        if(!file_exists($imgPath)) return false;
        return $imgPath;
    }

    /**
     * 销毁图片
     */
    public function __destruct(){
        if($this->image){
            imagedestroy($this->image); // 销毁一图像
        }
    }

}
